
/*
    引用：
    1、引用可以看作一个"已定义变量的别名"
    2、引用的语法：Type&  name = var;
    TIPS: (1)&在此不是求地址运算，而是起标识作用 (2)类型标识符是指目标变量的类型  (3)必须在声明引用变量时进行初始化
          (4)引用初始化之后不能改变  (5)不能有NULL引用。必须确保引用是和一块合法的存储单元关联
 */

#include <iostream>

using namespace std;

//虽然引用的定义是声明时必须初始化，但是结构体的特性是成员变量不能初始化，优先级更高(因为本质上，该引用变量还没有被声明)。
struct Test {
    int &a;
};

typedef struct Teacher {
    char name[64];
    int age;
} Teacher;



//普通变量作为函数的参数
void change1(Teacher pT) {
    pT.age = 99;
}

//指针作为函数的参数
void change2(Teacher *const pT) {
    pT->age = 18;
}

//引用作为函数的参数声明——可以不进行初始化
//该函数在编译后可认为是长这个样子的: void printfT2(Teacher* const pT)
void change3(Teacher &pT) {
    pT.age = 49;
}

int main() {
    int a = 10;
    //给变量a取一个别名b
    int &b = a;
    cout << "a:" << a << endl;
    cout << "b:" << b << endl;
    cout << "------------" << endl;
    //操作b就相当于操作a本身
    b = 100;
    cout << "a:" << a << endl;
    cout << "b:" << b << endl;
    cout << "------------" << endl;
    //一个变量可以有n个别名
    int &c = a;
    c = 200;
    cout << "a:" << a << endl;
    cout << "b:" << b << endl;
    cout << "c:" << c << endl;
    cout << "------------" << endl;
    //a,b,c的地址都是相同的
    cout << "a:" << &a << endl;
    cout << "b:" << &b << endl;
    cout << "c:" << &c << endl;
    cout << "------------" << endl;

    //使用引用的注意事项：
    //1) 引用必须初始化
    //编译异常: Declaration of reference variable 'ref' requires an initializer
    //int& ref; //报错:必须初始化引用
    //2) 引用一旦初始化，不要改变引用
    int d = 100;
    b = d; //不是b重新引用d，而是将d的值赋值给b，等价于b=100
    //a的值被改变了，为什么呢?
    cout << "a:" << a << endl;

    //引用的本质:
    //1、引用的本质在c++内部的实现是一个常指针。 Type& ref = val -> Type* const ref = &val;
    //2、c++编译器在编译过程中使用常指针作为引用的内部实现，因此"引用所占用的空间大小与指针相同"，只是这个过程是编译器内部实现，用户不可见。
    int &aRef = a; //自动转换为int* const aRef = &a;这也能说明引用为什么必须初始化
    aRef = 20; //内部发现aRef是引用，自动帮我们转换为: *aRef = 20;
    cout << "a:" << a << endl;

    //验证引用占用内存空间大小：
    //错误方式:
    char charValue = 'a';
    char &rcv = charValue;
    //这个结果并不是指针大小，而是被引用的那个数据类型的大小(sizeof求的是数据类型占用内存空间的大小)。
    cout << "引用占用的内存空间大小(错误方式):" << sizeof(rcv) << endl;
    //正确方式(通过结构体的特性):
    //TIPS:如果某些文章内提到引用不占用内存空间，那么该理论其实更多的可能是来自于编译器的优化
    //  (指若编译器发现引用，则将引用变量直接修改为被引用的变量，节约了内存的占用)。但是若没有来自编译器的优化，则按照本质其就是占用指针大小。
    cout << "引用占用的内存空间大小:" << sizeof(Test) << endl;

    struct Test test = {
            //引用在声明时必须用其它的变量进行初始化，否则语法分析异常: field 'a' of reference type 'int &' would not be initialized
            .a = d
    };


    Teacher t1;
    t1.age = 35;
    //将变量本身传入函数
    change1(t1);
    cout << "函数接收到的只是结构体里面的值，所以函数内部的修改对外部无效:" << t1.age << endl;
    //将变量的地址传入函数
    change2(&t1);
    cout << "函数接收到的是变量的地址，则可以通过指针修改里面的值:" << t1.age << endl;
    //看似是传了变量本身，但是函数的形参是引用，所以pT是t1的别名
    change3(t1);
    cout << "函数接收到了变量的引用，则内部通过引用修改了变量的值:" << t1.age << endl;


    //引用的意义:
    // 1)引用作为其他变量的别名而存在，因此在一些场合可以代替指针
    // 2)引用相对于指针来说具有更好的可读性和实用性
    // 使用场景: 如果引用对象是基础数据类型则与指针差不多，引用在函数形参为对象的引用以及函数返回值为对象的引用上使用非常广泛，在使用类和对象后更为明显。

    return 0;
}
